Hands-on Exercise 2

Author

Gautamgovan

Published

September 4, 2025

Modified

September 5, 2025

Overview

Spatial Point Pattern Analysis (SPPA) is the evaluation of the pattern or distribution of a set of points on a surface. The points may represent:

  • Events such as crimes, traffic accidents, or disease onsets, or
  • Business services (e.g., coffee shops and fast-food outlets) or facilities such as childcare centres and eldercare centres.

First-order Spatial Point Pattern Analysis (1st-SPPA) focuses on understanding the intensity or density of points across a study area. It examines how the distribution of points varies over space, essentially identifying trends or patterns in point density. This type of analysis deals with the individual locations of points and their distribution, without considering interactions between them.

Second-order spatial point pattern analysis examines the relationships between points, studying whether they are clustered, dispersed, or randomly distributed using tools like Ripley’s K function or nearest-neighbor analysis. It answers the question: “How do points interact with each other in space?”

1 First Order Spatial Point Pattern Analysis

In essence, 1st-SPPA helps answer questions such as:

  • Where are points most densely located within the study area?
  • Is point density uniform, or does it vary across space?
  • How spread out is the point pattern?

In this chapter, we will gain hands-on experience with spatstat to perform two commonly used 1st-SPPA methods.

The specific questions we would like to answer are as follows:

  • Are the childcare centres in Singapore randomly distributed throughout the country?
  • If the answer is not, then the next logical question is: where are the locations with higher concentration of childcare centres?

1.1 The Data

Child Care Services data from data.gov.sg, a point feature data providing both location and attribute information of childcare centres. Master Plan 2019 Subzone Boundary (No Sea), a polygon feature data providing information of URA 2019 Master Plan Planning Subzone boundary data.

geojson format were used

1.2 Installing and Loading the R packages

In this hands-on exercise, we will use five R packages:

  • sf: A modern package designed to import, manage, and process vector-based geospatial data in R.
  • spatstat: A powerful package for point pattern analysis. We will use it to perform 1st- and 2nd-order spatial point pattern analysis and derive kernel density estimation (KDE) layers.
  • terra: A fast and efficient package for working with raster and vector data, designed to replace the older raster package. In our exercise, we will use it to convert image outputs generated by spatstat into terra format.
  • tmap: A package for creating cartographic-quality static maps and interactive maps (via the leaflet API).
  • rvest: A package for web scraping, which we can use to harvest data directly from web pages.

We will install and load these packages using the following code:

pacman::p_load(sf, terra, spatstat, 
               tmap, rvest, tidyverse)

1.3 Importing and Wrangling Geospatial Data Sets

subzone_path   <- "data/geospatial/MasterPlan2019SubzoneBoundaryNoSeaGEOJSON.geojson"
childcare_path <- "data/geospatial/ChildCareServices.geojson"
# Read datasets
mpsz_sf      <- st_read(subzone_path, quiet = TRUE)
childcare_sf <- st_read(childcare_path, quiet = TRUE)

# Quick checks
print(st_geometry_type(mpsz_sf)[1])
[1] MULTIPOLYGON
18 Levels: GEOMETRY POINT LINESTRING POLYGON MULTIPOINT ... TRIANGLE
print(st_geometry_type(childcare_sf)[1])
[1] POINT
18 Levels: GEOMETRY POINT LINESTRING POLYGON MULTIPOINT ... TRIANGLE
#Validate geometry
mpsz_sf      <- st_make_valid(mpsz_sf)
childcare_sf <- st_make_valid(childcare_sf)
#Transform both datasets to Singapore SVY21 CRS (EPSG:3414)
target_epsg <- 3414

mpsz_sf      <- st_transform(mpsz_sf, target_epsg)
childcare_sf <- st_transform(childcare_sf, target_epsg)
#Confirm both are aligned
st_crs(mpsz_sf)$epsg
[1] 3414
st_crs(childcare_sf)$epsg
[1] 3414
identical(st_crs(mpsz_sf), st_crs(childcare_sf))
[1] TRUE
# Should return TRUE
#Optional tidying (only if these fields exist in subzone data)
if (all(c("SUBZONE_N","PLN_AREA_N") %in% names(mpsz_sf))) {
  mpsz_sf <- mpsz_sf |>
    filter(SUBZONE_N != "SOUTHERN GROUP",
           PLN_AREA_N != "WESTERN ISLANDS",
           PLN_AREA_N != "NORTH-EASTERN ISLANDS")
}

1.3.1 Mapping the geo spatial data sets

#Quick visual check
tmap_mode("plot")
tm_shape(mpsz_sf) + tm_polygons(alpha = 0.2) +
tm_shape(childcare_sf) + tm_dots(size = 0.03, col = "red")

Interactive point symbol map

tmap_mode('view')
tm_shape(childcare_sf)+
  tm_dots()
tmap_mode('plot')

1.4 Geospatial Data Wrangling

spatstat relies on its own specific data structures like ppp (planar point pattern) for point data and owin for observation windows. In this section, we will learn how to convert sf (Simple Features) objects into spatstat ppp and owin object.

1.4.1 Converting sf data frames to ppp class

The spatstat package requires point event data in ppp object form.
We can use the [as.ppp()] function from spatstat to convert childcare_sf into ppp format. class() of Base R will be used to verify the object class of childcare_ppp.

childcare_ppp <- as.ppp(childcare_sf)
class(childcare_ppp)
[1] "ppp"